package com.atguigu.tingshu.search.service.impl;

import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch._types.FieldValue;
import co.elastic.clients.elasticsearch._types.SortOrder;
import co.elastic.clients.elasticsearch._types.aggregations.Aggregate;
import co.elastic.clients.elasticsearch._types.query_dsl.BoolQuery;
import co.elastic.clients.elasticsearch._types.query_dsl.Query;
import co.elastic.clients.elasticsearch.core.SearchRequest;
import co.elastic.clients.elasticsearch.core.SearchResponse;
import co.elastic.clients.elasticsearch.core.search.Hit;
import com.alibaba.fastjson.JSON;
import com.atguigu.tingshu.album.client.AlbumInfoFeignClient;
import com.atguigu.tingshu.album.client.CategoryFeignClient;
import com.atguigu.tingshu.common.constant.RedisConstant;
import com.atguigu.tingshu.common.result.Result;
import com.atguigu.tingshu.model.album.*;
import com.atguigu.tingshu.model.search.AlbumInfoIndex;
import com.atguigu.tingshu.model.search.AttributeValueIndex;
import com.atguigu.tingshu.query.search.AlbumIndexQuery;
import com.atguigu.tingshu.search.repository.AlbumIndexRepository;
import com.atguigu.tingshu.search.service.SearchService;
import com.atguigu.tingshu.user.client.UserInfoFeignClient;
import com.atguigu.tingshu.vo.album.AlbumStatVo;
import com.atguigu.tingshu.vo.search.AlbumInfoIndexVo;
import com.atguigu.tingshu.vo.search.AlbumSearchResponseVo;
import com.atguigu.tingshu.vo.user.UserInfoVo;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;

import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.stream.Collectors;


@Slf4j
@Service
@SuppressWarnings({"all"})
public class SearchServiceImpl implements SearchService {


    @Autowired
    private AlbumInfoFeignClient albumInfoFeignClient;

    @Autowired
    private CategoryFeignClient categoryFeignClient;

    @Autowired
    private UserInfoFeignClient userInfoFeignClient;

    @Autowired
    private AlbumIndexRepository albumIndexRepository;

    @Autowired
    private ThreadPoolExecutor threadPoolExecutor;

    @Autowired
    private ElasticsearchClient elasticsearchClient;

    @Autowired
    private RedisTemplate redisTemplate;

    /**
     * 上架专辑
     *
     * @param albumId
     */
    @Override
    public void upperAlbum(Long albumId) {
        //创建索引库对象
        AlbumInfoIndex albumInfoIndex = new AlbumInfoIndex();

        CompletableFuture<AlbumInfo> albumInfoIndexCompletableFuture = CompletableFuture.supplyAsync(() -> {
            //获取专辑信息
            Result<AlbumInfo> albumInfoResult = albumInfoFeignClient.getAlbumInfo(albumId);
            Assert.notNull(albumInfoResult, "专辑结果为空");
            AlbumInfo albumInfo = albumInfoResult.getData();
            Assert.notNull(albumInfo, "专辑为空");
            BeanUtils.copyProperties(albumInfo, albumInfoIndex);
            return albumInfo;
        }, threadPoolExecutor);

        CompletableFuture<Void> attrCompletableFuture = CompletableFuture.runAsync(() -> {
            //获取专辑属性值信息
            Result<List<AlbumAttributeValue>> albumAttributeValueResult = albumInfoFeignClient.findAlbumAttributeValue(albumId);
            Assert.notNull(albumAttributeValueResult, "专辑属性值结果为空");
            List<AlbumAttributeValue> albumAttributeValueList = albumAttributeValueResult.getData();
            Assert.notNull(albumAttributeValueList, "专辑属性值为空");
            //赋值属性值信息
            if (!CollectionUtils.isEmpty(albumAttributeValueList)) {
                List<AttributeValueIndex> attributeValueIndexList = albumAttributeValueList.stream().map(albumAttributeValue -> {
                    AttributeValueIndex attributeValueIndex = new AttributeValueIndex();
                    attributeValueIndex.setAttributeId(albumAttributeValue.getAttributeId());
                    attributeValueIndex.setValueId(albumAttributeValue.getValueId());
                    return attributeValueIndex;
                }).collect(Collectors.toList());
                albumInfoIndex.setAttributeValueIndexList(attributeValueIndexList);
            }
        }, threadPoolExecutor);

        CompletableFuture<Void> categoryCompletableFuture = albumInfoIndexCompletableFuture.thenAcceptAsync(albumInfo -> {
            //根据三级分类获取分类信息
            Result<BaseCategoryView> categoryViewResult = categoryFeignClient.getCategoryView(albumInfo.getCategory3Id());
            Assert.notNull(categoryViewResult, "三级分类获取分类信息结果为空");
            BaseCategoryView baseCategoryView = categoryViewResult.getData();
            Assert.notNull(baseCategoryView, "三级分类获取分类信息为空");
            //赋值分类属性
            albumInfoIndex.setCategory1Id(baseCategoryView.getCategory1Id());
            albumInfoIndex.setCategory2Id(baseCategoryView.getCategory2Id());
            albumInfoIndex.setCategory3Id(baseCategoryView.getCategory3Id());
        }, threadPoolExecutor);

        CompletableFuture<Void> userCompletableFuture = albumInfoIndexCompletableFuture.thenAcceptAsync(albumInfo -> {
            //根据用户id获取到用户信息
            Result<UserInfoVo> userInfoVoResult = userInfoFeignClient.getUserInfoVo(albumInfo.getUserId());
            Assert.notNull(userInfoVoResult, "获取用户信息结果为空");
            UserInfoVo userInfoVo = userInfoVoResult.getData();
            Assert.notNull(userInfoVo, "获取用户信息为空");
            //赋值主播名称
            albumInfoIndex.setAnnouncerName(userInfoVo.getNickname());
        });
        CompletableFuture<Void> statCompletableFuture = CompletableFuture.runAsync(() -> {
            //根据专辑id获取统计量
            Result<AlbumStatVo> albumStatVoResult = albumInfoFeignClient.getStat(albumId);
            Assert.notNull(albumStatVoResult, "获取统计量结果为空");
            AlbumStatVo albumStatVo = albumStatVoResult.getData();
            Assert.notNull(albumStatVo, "获取统计量为空");
            //赋值统计量
            albumInfoIndex.setPlayStatNum(albumStatVo.getPlayStatNum());
            albumInfoIndex.setSubscribeStatNum(albumStatVo.getSubscribeStatNum());
            albumInfoIndex.setBuyStatNum(albumStatVo.getBuyStatNum());
            albumInfoIndex.setCommentStatNum(albumStatVo.getCommentStatNum());
        }, threadPoolExecutor);
        //获取统计量


        CompletableFuture.allOf(
                albumInfoIndexCompletableFuture,
                attrCompletableFuture,
                categoryCompletableFuture,
                userCompletableFuture,
                statCompletableFuture
        ).join();
        log.info("上架专辑：{}", albumInfoIndex);
        albumIndexRepository.save(albumInfoIndex);

    }

    /**
     * 下架专辑
     *
     * @param albumId
     */
    @Override
    public void lowerAlbum(Long albumId) {
        albumIndexRepository.deleteById(albumId);
    }

    /**
     * 根据关键字搜索专辑
     *
     * @param albumIndexQuery
     * @return
     */
    @Override
    public AlbumSearchResponseVo searchByKeyWord(AlbumIndexQuery albumIndexQuery) {

        //生成DSL语句，构建SearchRequest对象
        SearchRequest searchRequest = buildSearchRequest(albumIndexQuery);
        //执行search方法
        SearchResponse<AlbumInfoIndex> searchResponse = null;
        try {
            searchResponse = elasticsearchClient.search(searchRequest, AlbumInfoIndex.class);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        //将searchResponse 对象中的数据赋值给 AlbumSearchResponseVo
        AlbumSearchResponseVo albumSearchResponseVo = new AlbumSearchResponseVo();
        albumSearchResponseVo = this.parseResultDate(searchResponse);

        //    检索出来的商品信息
        //    private List<AlbumInfoIndexVo> list = new ArrayList<>();
        //
        //    private Long total;//总记录数
        //    private Integer pageSize;//每页显示的内容
        //    private Integer pageNo;//当前页面
        //    private Long totalPages;
        // 设置总数量
        albumSearchResponseVo.setTotal(searchResponse.hits().total().value());
        // 设置当前页码
        albumSearchResponseVo.setPageNo(albumIndexQuery.getPageNo());
        // 设置每页数量
        albumSearchResponseVo.setPageSize(albumIndexQuery.getPageSize());
        // 设置总页数 10 3 4   9 3 3
        albumSearchResponseVo.setTotalPages((albumSearchResponseVo.getTotal() + albumSearchResponseVo.getPageSize() - 1) / albumSearchResponseVo.getPageSize());

        return albumSearchResponseVo;
    }


    /**
     * 获取查询请求对象 - 生成 dsl 语句.
     *
     * @param albumIndexQuery
     * @return
     */
    private SearchRequest buildSearchRequest(AlbumIndexQuery albumIndexQuery) {

        // Create a new SearchRequest.Builder object
        SearchRequest.Builder searchRequestBulider = new SearchRequest.Builder();
        // Set the index to "albuminfo"
        searchRequestBulider.index("albuminfo");

        // Create a new Query.Builder object
        Query.Builder queryBuilder = new Query.Builder();
        // Create a new BoolQuery.Builder object
        BoolQuery.Builder boolQueryBuilder = new BoolQuery.Builder();

        //GET /albuminfo/_search
        //{
        //  "query": {
        //    "bool": {
        //      "should": [
        //        {
        //          "match": {
        //            "albumTitle": "小说"
        //          }
        //        },
        //        {
        //          "match": {
        //            "albumIntro": "小说"
        //          }
        //        }
        //      ],
        //      "filter": [
        //        {
        //          "term": {
        //            "category3Id": "1152"
        //          }
        //        }
        //      ]
        //    }
        //  }
        //}
        //# 1. 根据关键词 过滤
        if (StringUtils.isNotBlank(albumIndexQuery.getKeyword())) {
            boolQueryBuilder.should(s -> s.match(m -> m.field("albumTitle").query(albumIndexQuery.getKeyword())))
                    .should(s -> s.match(m -> m.field("albumIntro").query(albumIndexQuery.getKeyword())))
                    .should(s -> s.match(m -> m.field("announcerName").query(albumIndexQuery.getKeyword())));
            //高亮
            searchRequestBulider.highlight(h -> h.fields("albumTitle", hf -> hf.preTags("<span style=color:red>").postTags("</span>")));
        }
        //2. 根据分类属性id 过滤
        if (null != (albumIndexQuery.getCategory3Id())) {
            boolQueryBuilder.filter(f -> f.term(t -> t.field("category3Id").value(albumIndexQuery.getCategory3Id())));
        }
        if (null != albumIndexQuery.getCategory2Id()) {
            boolQueryBuilder.filter(f -> f.term(t -> t.field("category2Id").value(albumIndexQuery.getCategory2Id())));
        }
        if (null != albumIndexQuery.getCategory1Id()) {
            boolQueryBuilder.filter(f -> f.term(t -> t.field("category1Id").value(albumIndexQuery.getCategory1Id())));
        }
        //# 根据属性与属性值Id 进行过滤
        //GET /albuminfo/_search
        //{
        //  "query": {
        //    "bool": {
        //      "filter": [
        //        {
        //          "nested": {
        //            "path": "attributeValueIndexList",
        //            "query": {
        //              "bool": {
        //                "must": [
        //                  {
        //                    "term": {
        //                      "attributeValueIndexList.attributeId": {
        //                        "value": "15"
        //                      }
        //                    }
        //                  },
        //                  {
        //                    "term": {
        //                      "attributeValueIndexList.valueId": {
        //                        "value": "32"
        //                      }
        //                    }
        //                  }
        //                ]
        //              }
        //            }
        //          }
        //        }
        //      ]
        //    }
        //  }
        //}
        //3. 根据属性值进行过滤 属性id:属性值id
        List<String> attributeList = albumIndexQuery.getAttributeList();
        if (!CollectionUtils.isEmpty(attributeList)) {
            for (String attribute : attributeList) {
                String[] attributes = attribute.split(":");
                if (attributes != null && attributes.length == 2) {
                    boolQueryBuilder.filter(f -> f.nested(n -> n.path("attributeValueIndexList")
                            .query(q -> q.bool(b -> b.must(m -> m.term(t -> t.field("attributeValueIndexList.attributeId").value(attributes[0])))
                                    .must(m -> m.term(t -> t.field("attributeValueIndexList.valueId").value(attributes[1])))
                            ))
                    ));
                }
            }
        }
        //# 分页-排序-高亮
        //GET /albuminfo/_search
        //{
        //  "query": {
        //    "match": {
        //      "albumTitle": "小说"
        //    }
        //  },
        //  "from": 0,
        //  "size": 20,
        //  "sort": [
        //    {
        //      "playStatNum": {
        //        "order": "desc"
        //      }
        //    }
        //  ],
        //  "highlight": {
        //    "fields": {"albumTitle": {}},
        //    "pre_tags": ["<font color='red'>"],
        //    "post_tags": ["</font>"]
        //  }
        //}
        //  排序（综合排序[1:desc] 播放量[2:desc] 发布时间[3:desc]；asc:升序 desc:降序）
        String order = albumIndexQuery.getOrder();
        if (StringUtils.isNotBlank(order)) {
            String[] splits = order.split(":");
            if (order != null && splits.length == 2) {
                if ("1".equals(splits[0])) {
                    searchRequestBulider.sort(s -> s.field(f -> f.field("hotScore")
                            .order("asc".equals(splits[1]) ? SortOrder.Asc : SortOrder.Desc)));
                }
                if ("2".equals(splits[0])) {
                    searchRequestBulider.sort(s -> s.field(f -> f.field("playStatNum")
                            .order("asc".equals(splits[1]) ? SortOrder.Asc : SortOrder.Desc)));
                }
                if ("3".equals(splits[0])) {
                    searchRequestBulider.sort(s -> s.field(f -> f.field("createTime")
                            .order("asc".equals(splits[1]) ? SortOrder.Asc : SortOrder.Desc)));
                }
            }
        }

        //分页
        searchRequestBulider.from((albumIndexQuery.getPageNo() - 1) * albumIndexQuery.getPageSize());
        searchRequestBulider.size(albumIndexQuery.getPageSize());

        //  构建boolQuery
        BoolQuery boolQuery = boolQueryBuilder.build();
        queryBuilder.bool(boolQuery);

        //  构建query
        Query query = queryBuilder.build();
        searchRequestBulider.query(query);

        //  构建searchRequest
        SearchRequest searchRequest = searchRequestBulider.build();

        //  打印dsl 语句
        System.out.println("dsl:\t" + searchRequest.toString());

        //  返回searchRequest
        return searchRequest;
    }

    private AlbumSearchResponseVo parseResultDate(SearchResponse<AlbumInfoIndex> searchResponse) {
        //    检索出来的商品信息
        //    private List<AlbumInfoIndexVo> list = new ArrayList<>();
        //    给AlbumInfoIndexVo赋值
        AlbumSearchResponseVo albumSearchResponseVo = new AlbumSearchResponseVo();
        List<Hit<AlbumInfoIndex>> hitList = searchResponse.hits().hits();
        List<AlbumInfoIndexVo> albumInfoIndexVoList = hitList.stream().map(hit -> {
            AlbumInfoIndexVo albumInfoIndexVo = new AlbumInfoIndexVo();
            AlbumInfoIndex albumInfoIndex = hit.source();
            BeanUtils.copyProperties(albumInfoIndex, albumInfoIndexVo);
            //  判断当前用户是否根据关键词进行检索，应该获取到高亮字段.
            if (null != hit.highlight().get("albumTitle")) {
                //  获取高亮标题
                String albumTitle = hit.highlight().get("albumTitle").get(0);
                //  赋值
                albumInfoIndexVo.setAlbumTitle(albumTitle);
            }

            return albumInfoIndexVo;
        }).collect(Collectors.toList());
        albumSearchResponseVo.setList(albumInfoIndexVoList);
        return albumSearchResponseVo;
    }

    /**
     * 获取频道页数据
     *
     * @param category1Id
     * @return
     */
    @Override
    public List<Map<String, Object>> getChannelData(Long category1Id) {
        //思路：
        //1. 先根据一级分类Id获取到三级分类集合数据
        //2. 获取到三级分类Id集合
        //3. 根据三级分类Id 集合生成根据三级分类Id查询专辑数据
        //4. 根据三级分类Id聚合并获取到前6条数据并按照热度排名进行降序排列
        //5. 从聚合中获取到数据并封装到返回数据的集合中

        //  第一个要获取到分类对象； 第二个获取到专辑列表;
        //  根据一级分类Id 获取到置顶数据集合
        Result<List<BaseCategory3>> baseCategory3ListResult = categoryFeignClient.findTopBaseCategory3(category1Id);
        //  判断
        Assert.notNull(baseCategory3ListResult, "获取分类数据失败");
        //  三级分类列表数据
        List<BaseCategory3> baseCategory3List = baseCategory3ListResult.getData();
        //  判断
        Assert.notNull(baseCategory3List, "获取分类列表失败");
        //  将baseCategory3List 这个集合转换为map 集合 key=category3Id value = category3;
        //  建立对应关系 key = 三级分类Id , value = 三级分类对象
        Map<Long, BaseCategory3> baseCategory3Map = baseCategory3List.stream().collect(Collectors.toMap(BaseCategory3::getId,
                baseCategory3 -> baseCategory3));
        //  获取到三级分类Id集合
        List<Long> category3IdList = baseCategory3List.stream().map(baseCategory3 -> baseCategory3.getId()).collect(Collectors.toList());
        List<FieldValue> category3IdFieldValueList = category3IdList.stream().map(category3Id -> FieldValue.of(category3Id)).collect(Collectors.toList());
        //  编写dsl语句
        //  .value(List<FieldValue>)
        SearchResponse<AlbumInfoIndex> searchResponse = null;
        try {
            searchResponse = elasticsearchClient.search(s -> s.index("albuminfo")
                            .query(q -> q.terms(t -> t.field("category3Id")
                                    .terms(tm -> tm.value(category3IdFieldValueList))))
                            .aggregations("aggCategory3Id", a -> a.terms(t -> t.field("category3Id"))
                                    .aggregations("aggTopSix", a2 -> a2.topHits(th -> th.size(6)
                                            .sort(st -> st.field(f -> f.field("buyStatNum").order(SortOrder.Desc)))))
                            )
                    ,
                    AlbumInfoIndex.class);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        Aggregate aggCategory3Id = searchResponse.aggregations().get("aggCategory3Id");
        List<Map<String, Object>> list = aggCategory3Id.lterms().buckets().array().stream().map(bucket -> {
            //  创建map 集合
            Map<String, Object> hashMap = new HashMap<>();
            //  获取到了三级分类Id
            long category3Id = bucket.key();
            //  获取专辑列表
            Aggregate aggTopSix = bucket.aggregations().get("aggTopSix");
            //  获取专辑列表
            List<AlbumInfoIndex> albumInfoIndexList = aggTopSix.topHits().hits().hits().stream().map(hit -> {
                //  将字符串转换为某个对象：
                AlbumInfoIndex albumInfoIndex = JSON.parseObject(hit.source().toString(), AlbumInfoIndex.class);
                //  返回数据
                return albumInfoIndex;
            }).collect(Collectors.toList());
            //  存储三级分类对象
            hashMap.put("baseCategory3", baseCategory3Map.get(category3Id));
            //  获取专辑列表;
            hashMap.put("list", albumInfoIndexList);

            //  返回集合数据
            return hashMap;
        }).collect(Collectors.toList());
        //  返回数据
        return list;
    }

    /**
     * 更新排行榜
     */
    @Override
    public void updateLatelyAlbumRanking() {
        // 获取所有一级分类Id
        Result<List<BaseCategory1>> baseCategory1Result = categoryFeignClient.findAllCategory1();
        Assert.notNull(baseCategory1Result, "获取一级分类结果失败");
        List<BaseCategory1> baseCategory1List = baseCategory1Result.getData();
        Assert.notNull(baseCategory1List, "获取一级分类失败");
        for (BaseCategory1 baseCategory1 : baseCategory1List) {
            //  五个维度
            String[] rankingDimensionArray = new String[]{"hotScore", "playStatNum", "subscribeStatNum", "buyStatNum", "commentStatNum"};
            for (String rankingDimension : rankingDimensionArray) {
                //  获取当前维度下的排行榜
                SearchResponse<AlbumInfoIndex> searchResponse = null;
                try {
                    searchResponse = elasticsearchClient.search(s -> s.index("albuminfo")
                                    .query(q -> q.term(t -> t.field("category1Id").value(baseCategory1.getId())))
                                    .sort(st -> st.field(f -> f.field(rankingDimension).order(SortOrder.Desc)))
                            , AlbumInfoIndex.class);
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
                List<AlbumInfoIndex> albumInfoIndexList = searchResponse.hits().hits().stream().map(Hit::source).collect(Collectors.toList());
                // 将albumInfoIndexList存入Redis的哈希数据结构中，键为排行榜键前缀和baseCategory1的id组合
                redisTemplate.opsForHash().put(RedisConstant.RANKING_KEY_PREFIX + baseCategory1.getId(), rankingDimension, albumInfoIndexList);

//                redisTemplate.opsForHash().put(RedisConstant.RANKING_KEY_PREFIX+baseCategory1.getId(),rankingDimension,albumInfoIndexList);
            }
        }
    }

    /**
     * 获取排行榜
     *
     * @param baseCategory1Id
     * @param rankingDimension
     * @return
     */
    @Override
    public List<AlbumInfoIndex> findRankingList(Long baseCategory1Id, String rankingDimension) {
        return (List<AlbumInfoIndex>) redisTemplate.opsForHash().get(RedisConstant.RANKING_KEY_PREFIX + baseCategory1Id, rankingDimension);
    }
}
